package com.slidingmenu.lib;
import java.lang.reflect.Method;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.os.ParcelableCompat;
import android.support.v4.os.ParcelableCompatCreatorCallbacks;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import com.slidingmenu.lib.CustomViewAbove.OnPageChangeListener;
public class SlidingMenu extends RelativeLayout {
public static final String TAG = "SlidingMenu";
public static final int TOUCHMODE_MARGIN = 0;
public static final int TOUCHMODE_FULLSCREEN = 1;
public static final int LEFT = 0x0100;
public static final int RIGHT = 0x1000;
public static final int BOTH = LEFT | RIGHT;
private final CustomViewAbove mViewAbove;
private CustomViewBehind mViewBehindLeft;
private CustomViewBehind mViewBehindRight;
private OnOpenListener mOpenListener;
private OnCloseListener mCloseListener;
private boolean mSlidingEnabled;
private int mMode;
public static void attachSlidingMenu(Activity activity, SlidingMenu sm, boolean slidingTitle) {
if (sm.getParent() != null)
throw new IllegalArgumentException("SlidingMenu cannot be attached to another view when" +
" calling the static method attachSlidingMenu");
if (slidingTitle) {
// get the window background
TypedArray a = activity.getTheme().obtainStyledAttributes(new int[] {android.R.attr.windowBackground});
int background = a.getResourceId(0, 0);
// move everything into the SlidingMenu
ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();
ViewGroup decorChild = (ViewGroup) decor.getChildAt(0);
decor.removeAllViews();
// save ActionBar themes that have transparent assets
decorChild.setBackgroundResource(background);
sm.setContent(decorChild);
decor.addView(sm);
} else {
// take the above view out of
ViewGroup content = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
View above = content.getChildAt(0);
content.removeAllViews();
sm.setContent(above);
content.addView(sm, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
}
}
public interface OnOpenListener {
public void onOpen();
}
public interface OnOpenedListener {
public void onOpened(int side);
}
public interface OnCloseListener {
public void onClose();
}
public interface OnClosedListener {
public void onClosed();
}
public interface CanvasTransformer {
public void transformCanvas(Canvas canvas, float percentOpen);
}
public SlidingMenu(Context context) {
this(context, null);
}
public SlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// above view
LayoutParams aboveParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mViewAbove = new CustomViewAbove(context);
addView(mViewAbove, aboveParams);
mViewAbove.setOnPageChangeListener(new OnPageChangeListener() {
public static final int POSITION_LEFT_OPEN = 0;
public static final int POSITION_CLOSE = 1;
public static final int POSITION_RIGHT_OPEN = 2;
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) { }
@Override
public void onPageSelected(int position) {
if ((position == POSITION_LEFT_OPEN || position == POSITION_RIGHT_OPEN) && mOpenListener != null) {
mOpenListener.onOpen();
} else if (position == POSITION_CLOSE && mCloseListener != null) {
mCloseListener.onClose();
}
}
});
// now style everything!
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlidingMenu);
// set the above and behind views if defined in xml
int viewAbove = ta.getResourceId(R.styleable.SlidingMenu_viewAbove, -1);
if (viewAbove != -1) {
setContent(viewAbove);
}
int viewBehindLeft = ta.getResourceId(R.styleable.SlidingMenu_viewBehindLeft, -1);
if (viewBehindLeft != -1) {
mMode |= LEFT;
setViewBehind(viewBehindLeft, LEFT);
}
int viewBehindRight = ta.getResourceId(R.styleable.SlidingMenu_viewBehindRight, -1);
if (viewBehindRight != -1) {
mMode |= RIGHT;
setViewBehind(viewBehindRight, RIGHT);
}
int touchModeAbove = ta.getInt(R.styleable.SlidingMenu_touchModeAbove, TOUCHMODE_MARGIN);
setTouchModeAbove(touchModeAbove);
int touchModeBehind = ta.getInt(R.styleable.SlidingMenu_touchModeBehind, TOUCHMODE_MARGIN);
setTouchModeBehind(touchModeBehind);
int offsetBehind = (int) ta.getDimension(R.styleable.SlidingMenu_behindOffset, -1);
int widthBehind = (int) ta.getDimension(R.styleable.SlidingMenu_behindWidth, -1);
if (offsetBehind != -1 && widthBehind != -1) {
throw new IllegalStateException("Cannot set both behindOffset and behindWidth for a SlidingMenu");
} else if (offsetBehind != -1) {
setBehindOffset(offsetBehind, mMode);
} else if (widthBehind != -1) {
setBehindWidth(widthBehind, mMode);
}
float scrollOffsetBehind = ta.getFloat(R.styleable.SlidingMenu_behindScrollScale, 0.33f);
setBehindScrollScale(scrollOffsetBehind, mMode);
int shadowRes = ta.getResourceId(R.styleable.SlidingMenu_shadowDrawable, -1);
if (shadowRes != -1) {
setShadowDrawable(shadowRes, mMode);
}
int shadowWidth = (int) ta.getDimension(R.styleable.SlidingMenu_shadowWidth, 0);
setShadowWidth(shadowWidth);
boolean fadeEnabled = ta.getBoolean(R.styleable.SlidingMenu_behindFadeEnabled, true);
setFadeEnabled(fadeEnabled);
float fadeDeg = ta.getFloat(R.styleable.SlidingMenu_behindFadeDegree, 0.66f);
setFadeDegree(fadeDeg);
boolean selectorEnabled = ta.getBoolean(R.styleable.SlidingMenu_selectorEnabled, false);
setSelectorEnabled(selectorEnabled);
int selectorRes = ta.getResourceId(R.styleable.SlidingMenu_selectorDrawable, -1);
if (selectorRes != -1)
setSelectorDrawable(selectorRes);
ta.recycle();
}
private void initializeLeft() {
if (mViewBehindLeft == null) {
LayoutParams behindParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mViewBehindLeft = new CustomViewBehind(getContext(), CustomViewBehind.LEFT);
addView(mViewBehindLeft, 0, behindParams);
mViewAbove.setViewBehindLeft(mViewBehindLeft);
mViewBehindLeft.setCustomViewAbove(mViewAbove);
}
}
private void initializeRight() {
if (mViewBehindRight == null) {
LayoutParams behindParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mViewBehindRight = new CustomViewBehind(getContext(), CustomViewBehind.RIGHT);
addView(mViewBehindRight, 0, behindParams);
mViewAbove.setViewBehindRight(mViewBehindRight);
mViewBehindRight.setCustomViewAbove(mViewAbove);
}
}
public void removeViewBehind(int side) {
if (isLeft(side)) {
mViewAbove.setViewBehindLeft(null);
removeView(mViewBehindLeft);
mViewBehindLeft = null;
} else if (isRight(side)) {
mViewAbove.setViewBehindRight(null);
removeView(mViewBehindRight);
mViewBehindRight = null;
}
}
public void setContent(int res) {
setContent(LayoutInflater.from(getContext()).inflate(res, null));
}
public void setContent(View v) {
mViewAbove.setContent(v);
mViewAbove.invalidate();
showAbove();
}
public void setViewBehind(int res, int side) {
setViewBehind(LayoutInflater.from(getContext()).inflate(res, null), side);
}
public void setViewBehind(View v, int side) {
checkSide(side);
if (side == LEFT) {
initializeLeft();
if (v == null) {
removeViewBehind(LEFT);
} else {
initializeLeft();
mViewBehindLeft.setContent(v);
mViewBehindLeft.invalidate();
}
} else {
initializeRight();
if (v == null) {
removeViewBehind(RIGHT);
} else {
initializeRight();
mViewBehindRight.setContent(v);
mViewBehindRight.invalidate();
}
}
}
private void checkSide(int side) {
if (!isLeft(side) && !isRight(side))
throw new IllegalArgumentException("Side must be SlidingMenu.LEFT or SlidingMenu.RIGHT");
}
private void checkSideStrict(int side) {
checkSide(side);
if (isLeft(side) && mViewBehindLeft == null)
throw new IllegalArgumentException("You have to add a behind left " +
"view before you can reference it");
if (isRight(side) && mViewBehindRight == null)
throw new IllegalArgumentException("You have to add a behind right " +
"view before you can reference it");
}
private boolean isLeft(int side) {
return (side & LEFT) == LEFT;
}
private boolean isRight(int side) {
return (side & RIGHT) == RIGHT;
}
public void setSlidingEnabled(boolean b) {
mViewAbove.setSlidingEnabled(b);
}
public boolean isSlidingEnabled() {
return mViewAbove.isSlidingEnabled();
}
/**
* Shows the behind view
*/
public void showBehind(int side) {
checkSideStrict(side);
if (isLeft(side) && isRight(side))
throw new IllegalArgumentException("Can't show both left and right");
if (isLeft(side)) {
mViewAbove.setCurrentItem(0);
} else if (isRight(side)) {
mViewAbove.setCurrentItem(2);
}
}
/**
* Shows the above view
*/
public void showAbove() {
mViewAbove.setCurrentItem(1);
}
/**
*
* @return Whether or not the behind view is showing
*/
public boolean isBehindShowing() {
return mViewAbove.getCurrentItem() == 0 || mViewAbove.getCurrentItem() == 2;
}
/**
*
* @return The margin on the right of the screen that the behind view scrolls to
*/
public int getBehindOffset(int side) {
checkSideStrict(side);
if (isLeft(side)) {
return ((RelativeLayout.LayoutParams)mViewBehindLeft.getLayoutParams()).rightMargin;
} else {
return ((RelativeLayout.LayoutParams)mViewBehindRight.getLayoutParams()).leftMargin;
}
}
/**
*
* @param i The margin on the right of the screen that the behind view scrolls to
*/
public void setBehindOffset(int i, int side) {
checkSideStrict(side);
if (isLeft(side)) {
// behind left
RelativeLayout.LayoutParams params = ((RelativeLayout.LayoutParams)mViewBehindLeft.getLayoutParams());
int bottom = params.bottomMargin;
int top = params.topMargin;
int left = params.leftMargin;
params.setMargins(left, top, i, bottom);
}
if (isRight(side)) {
// behind right
RelativeLayout.LayoutParams params = ((RelativeLayout.LayoutParams)mViewBehindRight.getLayoutParams());
int bottom = params.bottomMargin;
int top = params.topMargin;
int right = params.rightMargin;
Log.v(TAG, "left margin" + i);
params.setMargins(i, top, right, bottom);
}
requestLayout();
showAbove();
}
/**
*
* @param res The dimension resource to be set as the behind offset
*/
public void setBehindOffsetRes(int res, int side) {
int i = (int) getContext().getResources().getDimension(res);
setBehindOffset(i, side);
}
@SuppressWarnings("deprecation")
public void setBehindWidth(int i, int side) {
int width;
Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
try {
Class<?> cls = Display.class;
Class<?>[] parameterTypes = {Point.class};
Point parameter = new Point();
Method method = cls.getMethod("getSize", parameterTypes);
method.invoke(display, parameter);
width = parameter.x;
} catch (Exception e) {
width = display.getWidth();
}
setBehindOffset(width-i, side);
}
/**
*
* @return The scale of the parallax scroll
*/
public float getBehindScrollScale(int side) {
return mViewAbove.getScrollScale(side);
}
/**
*
* @param f The scale of the parallax scroll (i.e. 1.0f scrolls 1 pixel for every
* 1 pixel that the above view scrolls and 0.0f scrolls 0 pixels)
*/
public void setBehindScrollScale(float f, int side) {
mViewAbove.setScrollScale(f, side);
}
public void setBehindCanvasTransformer(CanvasTransformer t, int side) {
checkSideStrict(side);
if (isLeft(side)) {
mViewBehindLeft.setCanvasTransformer(t);
}
if (isRight(side)) {
mViewBehindRight.setCanvasTransformer(t);
}
}
public int getTouchModeAbove() {
return mViewAbove.getTouchMode();
}
public void setTouchModeAbove(int i) {
if (i != TOUCHMODE_FULLSCREEN && i != TOUCHMODE_MARGIN) {
throw new IllegalArgumentException("TouchMode must be set to either" +
"TOUCHMODE_FULLSCREEN or TOUCHMODE_MARGIN.");
}
mViewAbove.setTouchMode(i);
}
public void setTouchModeAbove(int i, int side) {
if (i != TOUCHMODE_FULLSCREEN && i != TOUCHMODE_MARGIN) {
throw new IllegalArgumentException("TouchMode must be set to either" +
"TOUCHMODE_FULLSCREEN or TOUCHMODE_MARGIN.");
}
mViewAbove.setTouchMode(i, side);
}
public int getTouchModeBehind() {
return mViewAbove.getTouchModeBehind();
}
public void setTouchModeBehind(int i) {
if (i != TOUCHMODE_FULLSCREEN && i != TOUCHMODE_MARGIN) {
throw new IllegalArgumentException("TouchMode must be set to either " +
"TOUCHMODE_FULLSCREEN or TOUCHMODE_MARGIN.");
}
mViewAbove.setTouchModeBehind(i);
}
public void setShadowDrawable(int resId, int side) {
mViewAbove.setShadowDrawable(resId, side);
}
public void setShadowWidthRes(int resId) {
setShadowWidth((int)getResources().getDimension(resId));
}
public void setShadowWidth(int pixels) {
mViewAbove.setShadowWidth(pixels);
}
public void setFadeEnabled(boolean b) {
mViewAbove.setBehindFadeEnabled(b);
}
public void setFadeDegree(float f) {
mViewAbove.setBehindFadeDegree(f);
}
public void setSelectorEnabled(boolean b) {
mViewAbove.setSelectorEnabled(true);
}
public void setSelectedView(View v) {
mViewAbove.setSelectedView(v);
}
public void setSelectorDrawable(int res) {
mViewAbove.setSelectorDrawable(BitmapFactory.decodeResource(getResources(), res));
}
public void setSelectorDrawable(Bitmap b) {
mViewAbove.setSelectorDrawable(b);
}
public void setOnOpenListener(OnOpenListener listener) {
mViewAbove.setOnOpenListener(listener);
mOpenListener = listener;
}
public void setOnCloseListener(OnCloseListener listener) {
mViewAbove.setOnCloseListener(listener);
mCloseListener = listener;
}
public void setOnOpenedListener(OnOpenedListener listener) {
mViewAbove.setOnOpenedListener(listener);
}
public void setOnClosedListener(OnClosedListener listener) {
mViewAbove.setOnClosedListener(listener);
}
private static class SavedState extends BaseSavedState {
boolean mBehindShowing;
int mMode;
public SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeValue(mBehindShowing);
out.writeInt(mMode);
}
public static final Parcelable.Creator<SavedState> CREATOR
= ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in, ClassLoader loader) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
});
SavedState(Parcel in) {
super(in);
mBehindShowing = (Boolean) in.readValue(null);
mMode = in.readInt();
}
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.mBehindShowing = isBehindShowing();
ss.mMode = mMode;
return ss;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState)state;
super.onRestoreInstanceState(ss.getSuperState());
if (ss.mBehindShowing) {
showBehind(ss.mMode);
} else {
showAbove();
}
}
@Override
protected boolean fitSystemWindows(Rect insets) {
int leftPadding = getPaddingLeft() + insets.left;
int rightPadding = getPaddingRight() + insets.right;
int topPadding = insets.top;
int bottomPadding = insets.bottom;
this.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
// return false to propagate the fit to our child views
return false;
}
}